package sdk
/*
 #include "../../../Cosmos.App.Sdk.Native/Identify.c"
*/
import "C"
import (
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"os"
	"path"
	"runtime"
	"strconv"
	"strings"
	"unsafe"

	cry "Cosmos.Communication/infrastructure/crypto"
	"github.com/go-kratos/kratos/v2/log"

	"Cosmos.Communication/internal/biz"
	"Cosmos.Communication/internal/conf"
	"Cosmos.Communication/internal/service"
)

var (
	instance                  *Role
	vendorPid                 int
	processArgu_processorAddr string
	processArgu_vendorAddr    string
)

func Info(a ...interface{}) {
	data, err := json.Marshal(a)
	if err != nil {
		log.Info("Info Marshal Error:", err)
		return
	}
	log.Info(string(data))
}

// 校验身份
func VerifyIdentity(productID string) bool {
	if len(productID) == 0 {
		// 若宿主自身的 身份ID 为空，则表示 宿主不做cosmos校验
		return true
	}

	// 获取 cosmos 身份ID
	cosmosID := C.GoString(C.GetCosmosUuid())
	log.Info("Cosmos_cosmosID:", cosmosID)
	if productID != cosmosID {
		return false
	}

	return true
}

// 校验签名
func VerifySignature() bool {
	if runtime.GOOS != "linux" {
		// 非linux系统不做校验
		return true
	} 

	return true
}

// 身份签名校验
func VerifyIdentitySign(productID string) bool {	
	if !VerifyIdentity(productID) {
		return false
	}

	if !VerifySignature() {
		return false
	}

	return true
}

// #region Cosmos_InitializeEnvironment
func Cosmos_InitializeEnvironment(environment *Cosmos_EnvironmentCreationParameters, handler unsafe.Pointer) (result *biz.Result[string]) {
	result = &biz.Result[string]{
		Code: 200,
	}
	var err error

	log.Info("Cosmos_InitializeEnvironment Start")
	if environment != nil && environment.Responsibility != nil {
		log.Info("Cosmos_InitializeEnvironment Responsibility:", environment.Responsibility, handler)
		Info("Cosmos_InitializeEnvironment environment Param:", environment.Client, environment.Developer, environment.Globalization, environment.Proxy, environment.Visual, environment.WebView)
	}

	// 参数校验
	if environment == nil || environment.Client == nil || environment.Developer == nil || environment.WebView == nil {
		result.Code = 400
		result.Message = "参数非法"
		return
	}

	// 初始化日志
	logConfig := getLogConfig(environment.Client.Id)
	logger := InitLogger(logConfig.LogFilePath, logConfig.LogMaxSize, logConfig.LogMaxAge, logConfig.LogMaxBackups, logConfig.LogLevel)

	// 启动grpc服务
	conf := &conf.Config{
		Processor: &conf.Processor{Grpc: &conf.Grpc{}},
		Vendor:    &conf.Vendor{Grpc: &conf.Grpc{}, Path: environment.Client.CosmosMainAppPath},
	}
	conf.Processor.Grpc.Addr, conf.Vendor.Grpc.Addr = getGrpcAddress(environment)
	instance = CreateProcesorRole(conf, logger)
	go instance.RunRole()

	// 注册回调
	service.RegisterResponsibility(environment.Responsibility)
	service.RegisterEnvironmentEventHandler(handler)

	// 设置宿主自身 uuid
	productID := "yinhe-99406f7c-f074-4275-80c4-08d39106989c-yinhe"
	
	// 校验小核 uuid
	if !VerifyIdentitySign(productID){
		result.Code = 400
		result.Message = "身份校验失败"
		log.Info("Cosmos_VerifyIdentitySign:", "身份校验失败")
		return
	}

	// 命令行启动小核
	vendorPid, err = startProcess(environment, productID)
	if err != nil {
		result.Code = 500
		result.Message = err.Error()
	}
	log.Info("Cosmos_InitializeEnvironment:", "小核启动成功")
	return
}
func getLogConfig(clientId string) (config *ArgumentExtraField) {
	config = &ArgumentExtraField{}
	fields := strings.Split(clientId, "```")
	if len(fields) < 2 {
		return
	}

	extraByteArray, err := base64.StdEncoding.DecodeString(fields[1])
	if err != nil {
		return
	}

	err = json.Unmarshal(extraByteArray, &config)
	if err != nil {
		return
	}

	return
}
func startProcess(environment *Cosmos_EnvironmentCreationParameters, productID string) (vendorPid int, err error) {
	env := os.Environ()
	procAttr := &os.ProcAttr{
		Env: env,
		Files: []*os.File{
			os.Stdin,
			os.Stdout,
			os.Stderr,
		},
	}
	argv := getProcessStartArgument(environment, productID)
	log.Info(environment.Client.CosmosMainAppPath, argv)
	vendorProcess, err := os.StartProcess(
		environment.Client.CosmosMainAppPath,
		argv,
		procAttr)
	if vendorProcess != nil {
		vendorPid = vendorProcess.Pid
	}
	return
}
func getProcessStartArgument(
	environment *Cosmos_EnvironmentCreationParameters, productID string) (arg []string) {
	arg = make([]string, 11)
	arg[0] = ""
	arg[1] = fmt.Sprintf("-pe=%s", processArgu_processorAddr)
	arg[2] = fmt.Sprintf("-ce=%s", processArgu_vendorAddr)
	arg[3] = fmt.Sprintf("-ap=%s", environment.Developer.AppProviderMode)
	arg[4] = fmt.Sprintf("-rm=%s", environment.Developer.RuntimeMode)
	arg[5] = fmt.Sprintf("-gm=%s", environment.Developer.GuiMode)
	arg[6] = fmt.Sprintf("-pp=%s", strconv.Itoa(os.Getpid()))
	arg[7] = fmt.Sprintf("-cd=%s", environment.WebView.CefDirectory+","+environment.WebView.CefResourcesDirectory+","+environment.WebView.CefLocaleDirectory)
	arg[8] = fmt.Sprintf("-gd=%s", strconv.Itoa(environment.Client.HighDpiMode))
	arg[9] = fmt.Sprintf("-ext=%s", environment.Client.Id)
	arg[10] = fmt.Sprintf("-uid=%s", productID)
	return
}
func getGrpcAddress(environment *Cosmos_EnvironmentCreationParameters) (processor, vendor string) {
	if runtime.GOOS == "windows" {
		return getPipeName(environment)
	}

	return getUnixAddress(environment)
}
func getPipeName(environment *Cosmos_EnvironmentCreationParameters) (processor, vendor string) {
	var (
		pipeName = `\\.\pipe\`
	)

	var vendorPathMd5 = cry.MD5(environment.Client.CosmosMainAppPath)
	processor = pipeName + strconv.Itoa(os.Getpid())
	vendor = pipeName + vendorPathMd5

	processArgu_processorAddr = strconv.Itoa(os.Getpid())
	processArgu_vendorAddr = vendorPathMd5
	return
}
func getUnixAddress(environment *Cosmos_EnvironmentCreationParameters) (processor, vendor string) {
	var dir = path.Dir(environment.Client.CosmosMainAppPath)
	var vendorPathMd5 = cry.MD5(environment.Client.CosmosMainAppPath)
	processor = path.Join(dir, strconv.Itoa(os.Getpid())+".sock")
	vendor = path.Join(dir, vendorPathMd5+".sock")

	processArgu_processorAddr = processor
	processArgu_vendorAddr = vendor
	return
}

type Cosmos_EnvironmentCreationParameters struct {
	Client         *Cosmos_ClientParameters
	Proxy          *Cosmos_ProxyParameters
	WebView        *Cosmos_WebViewParameters
	Developer      *Cosmos_DeveloperParameter
	Globalization  *Cosmos_GlobalizationParameters
	Visual         *Cosmos_VisualParameters
	Responsibility unsafe.Pointer
}
type Cosmos_ClientParameters struct {
	Id                string
	Sku               string
	Version           string
	CosmosMainAppPath string
	HighDpiMode       int
}
type Cosmos_ProxyParameters struct {
}
type Cosmos_WebViewParameters struct {
	CefDirectory          string
	CefResourcesDirectory string
	CefLocaleDirectory    string
	CefExtraParameters    string
}
type Cosmos_DeveloperParameter struct {
	RuntimeMode         string
	GuiMode             string
	CosmosExecutiveFile string
	AppProviderMode     string
	AppProviderUrl      string
	Timeout             string
}
type Cosmos_GlobalizationParameters struct {
	RegionId   string
	LanguageId string
	TimeZoneId string
	Currency   string
}

type Cosmos_VisualParameters struct {
	GuiThemeFrameworks  string
	GuiThemeColorScheme string
}
type ArgumentExtraField struct {
	Account       string `json:"account"`
	Md5           string `json:"md5"`
	LogFilePath   string `json:"logfilepath"`
	LogMaxSize    int    `json:"logmaxsize"`
	LogMaxAge     int    `json:"logmaxage"`
	LogMaxBackups int    `json:"logMaxBackups"`
	LogLevel      int    `json:"loglevel"`
}

//#endregion

// #region Cosmos_UninitializeEnvironment
func Cosmos_UninitializeEnvironment() (result *biz.Result[string]) {
	result = &biz.Result[string]{
		Code: 200,
	}
	var err error

	Cleanup()
	err = instance.Stop()
	if err != nil {
		result.Code = 500
		result.Message = err.Error()
	}
	return
}

// # endregion

// #region Cosmos_CreateSession
func Cosmos_CreateSession(parameters *Cosmos_SessionCreationParameters) (result *biz.Result[*Cosmos_Session]) {
	result = &biz.Result[*Cosmos_Session]{
		Code: 200,
	}

	// 参数校验

	result.Data = &Cosmos_Session{
		ClientHandle: os.Getppid(),
		ServerHandle: vendorPid,
	}
	return
}

type Cosmos_SessionCreationParameters struct {
	Account *Cosmos_AccountParameters
	Cookie  *Cosmos_CookieParameters
}
type Cosmos_AccountParameters struct {
}
type Cosmos_CookieParameters struct {
}
type Cosmos_Session struct {
	ClientHandle int
	ServerHandle int
}

//#endregion

// #region Cosmos_Notify
func Cosmos_Notify(meta *biz.Meta, request *biz.NotifyRequest) (result *biz.Result[*biz.NotifyResponse]) {
	result = &biz.Result[*biz.NotifyResponse]{
		Code: 200,
	}
	var err error

	// 参数校验
	if meta == nil || request == nil {
		result.Code = 400
		result.Message = "notify vendor bad request"
		return
	}

	var ctx = context.Background()
	ctx = meta.Set(ctx)
	result, err = instance.Notify(ctx, request)
	if err != nil {
		result.Code = 500
		result.Message = err.Error()
	}
	return
}

//#endregion

// #region Cosmos_Invoke
func Cosmos_Invoke(meta *biz.Meta, request *biz.InvokeRequest) (result *biz.Result[*biz.InvokeResponse]) {
	result = &biz.Result[*biz.InvokeResponse]{
		Code: 200,
	}
	var err error

	// 参数校验
	if meta == nil || request == nil {
		result.Code = 400
		result.Message = "invoke bad request"
		return
	}

	var ctx = context.Background()
	//ctx = meta.Set(ctx)
	result, err = instance.Invoke(ctx, request)
	if err != nil {
		result.Code = 500
		result.Message = err.Error()
	}
	return
}

//#endregion

// #region Cosmos_PushSubscriptionData
func Cosmos_PushSubscriptionData(meta *biz.Meta, request *biz.DataFrame) (result *biz.Result[string]) {
	result = &biz.Result[string]{
		Code: 200,
	}
	var err error

	// 参数校验
	if meta == nil || request == nil || request.Ids == nil {
		result.Code = 400
		result.Message = "push bad request"
		return
	}

	var ctx = context.Background()
	ctx = meta.Set(ctx)
	err = instance.Push(ctx, request)
	if err != nil {
		result.Code = 500
		result.Message = err.Error()
	}
	return
}

//#endregion

// #region Cosmos_Query
func Cosmos_Query(meta *biz.Meta, request *biz.DataQueryRequest) (result *biz.Result[*biz.DataQueryResponse]) {
	result = &biz.Result[*biz.DataQueryResponse]{
		Code: 200,
	}
	var err error

	// 参数校验
	if meta == nil || request == nil {
		result.Code = 400
		result.Message = "query bad request"
		return
	}

	var ctx = context.Background()
	ctx = meta.Set(ctx)
	result, err = instance.Query(ctx, request)
	if err != nil {
		result.Code = 500
		result.Message = err.Error()
	}
	return
}

// #endregion
